<?php
/**
 * send email with attachments
 * @package simplemail
 * @version 1.3
 * @author Tetsuo
 * @contributor j.ducastel
 */
class SimpleMail {

    /**
     * constructor
     * @param string $from sender's address
     * @param string $to target address
     * @param string $subject
     * @param string $content
     */
    function SimpleMail($from=null,$to=null,$subject=null,$content=null) {
        if ($from)
            $this->setFrom($from);
        if ($to)
            $this->addRecipient($to);
        if ($subject)
            $this->setSubject($subject);
        if ($content)
            $this->addContent($content);
    }

    /**
     * add recipient
     * @param string $email
     * @param string $name
     * @return bool
     */
    function addRecipient($email,$name='') {
        $ad=new SimpleMailAddress($email,$name);
        if (!$ad->isValid())
            return false;
        $this->to[]=$ad;
        return true;
    }

    /**
     * sets sender address
     * @param string $email
     * @return bool
     */
    function setFrom($email,$name='') {
        $ad=new SimpleMailAddress($email,$name);
        if (!$ad->isValid())
            return false;
        $this->from=$ad;
        return true;
    }
    function addFrom($email,$name=null) {
        return $this->setFrom($email,$name);
    }

    /**
     * add carbon copy recipient
     * @param string $email
     * @param string $name
     * @return bool
     */
    function addCc($email,$name='') {
        $ad=new SimpleMailAddress($email,$name);
        if (!$ad->isValid())
            return false;
        $this->cc[]=$ad;
        return true;
    }

    /**
     * add black carbon copy recipient
     * @param string $email
     * @param string $name
     * @return bool
     */
    function addBcc($email,$name='') {
        $ad=new SimpleMailAddress($email,$name);
        if (!$ad->isValid())
            return false;
        $this->bcc[]=$ad;
        return true;
    }

    /**
     * set subject
     * @param string $subject
     * @return bool
     */
    function setSubject($subject) {
        if (!is_string($subject) or !$subject)
            return false;
        $this->subject = $subject;
        return true;
    }
    function addSubject($subject) {
        return $this->setSubject($subject);
    }

    /**
     * add some content to body
     * @param string $content
     * @return bool
     */
    function addContent($content) {
        $this->text.=$content;
        return true;
    }

    function addreturnpath($return) {
        $tmp=$this->makenameplusaddress($return,'');
        if ( !$tmp ) { $this->error_log("Return-Path: error"); return FALSE; }
        $this->returnpath = $return;
        return TRUE;
    }

    /**
     * sets reply-to address
     * @param string $email
     * @return bool
     */
    function setReplyTo($email) {
        $ad=new SimpleMailAddress($email);
        if (!$ad->isValid())
            return false;
        $this->reply=$ad;
        return true;
    }

    /**
     * attach a file
     * @param string $path
     * @param string $name name given to file
     * @return bool
     */
    function addAttachment($path, $filename=null) {
        // if filename not given, taking basename
        if (!is_string($filename) or !$filename)
            $filename=basename($path);
        $this->attachement[$filename]=array('path'=>$path);
        return true;
    }
    function addAttachement($path,$filename=null) {
        return $this->addAttachment($path,$filename);
    }

    // les attachements html
    function addhtmlattachement($path,$cid='',$contenttype='',$filename=null) {
        // if filename not given, taking basename
        if (!is_string($filename) or !$filename)
            $filename=basename($path);
        $this->attachement[$filename]=array(
            'path'=>$path
            ,'cid'=>$cid
            ,'contenttype'=>$contenttype);
        return true;
    }

    /**
     * sends email
     * @return bool
     */
    function send() {
        $this->makebody();
        $this->makeheader();
        switch($this->set_mode)    {
            case 'php' :
                return $this->phpmail();
                break;
            case 'socket':
                return $this->socketmailloop();
                break;
        }
        // return TRUE;
    }
    function sendMail() {
        return $this->send();
    }

/*------------------------------------------------------------------------------
 private parts
------------------------------------------------------------------------------*/

    /**
     * @var string
     */
    var $subject;

    /**
     * @var array(SimpleMailAddress)
     */
    var $to = array();
    var $bcc = array();
    var $cc = array();
    var $reply = array();

    /**
     * @var SimpleMailAddress
     */
    var $from;
    var $hfrom;

    var $Xsender;
    var $ErrorsTo;
    var $XMailer = 'PHP';
    var $XPriority = 3;

    var $set_mode='php';

    var $text;
    var $html;
    /**
     * @var array(filename=>array(path=>,cid=>))
     */
    var $attachement=array();
    var $htmlattachement=array();

    // var $recipient;

    var $body;
    var $headers;
    var $error_log;
        var $connect;

    var $default_charset = 'iso-8859-1';

    var $B1B = "----=_001";
    var $B2B = "----=_002";
    var $B3B = "----=_003";

    /**
     * encode file as string to add to email body
     * @param array $attachement(filename => path)
     * filename will be the name given to file
     * @param string $B boundary ?
     * @return string
     */
    function writeAttachement(&$attachement,$B) {
        $message = '';
        if ( !empty($attachement) ) {
            foreach($attachement as $FileName => $AttmFile){
                $message .= "\n--".$B."\n";

                if (!empty($AttmFile['cid'])) {
                    $message .= "Content-Type: {$AttmFile['contenttype']};\n name=\"".$FileName."\"\n";
                    $message .= "Content-Transfer-Encoding: base64\n";
                    $message .= "Content-ID: <{$AttmFile['cid']}>\n";
                    $message .= "Content-Disposition: inline;\n filename=\"".$FileName."\"\n\n";
                } else {
                    $message .= "Content-Type: application/octetstream;\n name=\"".$FileName."\"\n";
                    $message .= "Content-Transfer-Encoding: base64\n";
                    $message .= "Content-Disposition: attachment;\n filename=\"".$FileName."\"\n\n";
                }

                $fd=fopen ($AttmFile['path'], "rb");
                $FileContent=fread($fd,filesize($AttmFile['path']));
                fclose ($fd);

                $FileContent = chunk_split(base64_encode($FileContent));
                $message .= $FileContent;
                $message .= "\n\n";
            }
            $message .= "\n--".$B."--\n";
        }
        return $message;
    }

    function BodyLineWrap($Value) {
        return wordwrap($Value, 78, "\n ");
    }

    /**
     * builds email body from properties
     * @return string
     */
    function makeBody() {
        $message='';
        if ( !$this->html && $this->text && !empty($this->attachement) ) {

            //Messages start with text/html alternatives in OB
            $message ="This is a multi-part message in MIME format.\n";
            $message.="\n--".$this->B1B."\n";

            $message.="Content-Type: text/plain; charset=\"iso-8859-1\"\n";
            $message.="Content-Transfer-Encoding: quoted-printable\n\n";
            // plaintext goes here
            $message.=$this->BodyLineWrap($this->text)."\n\n";

            $message.=$this->writeattachement($this->attachement,$this->B1B);

        }
        elseif ( !$this->html && $this->text && empty($this->attachement) ) {

            // plaintext goes here
            $message.=$this->BodyLineWrap($this->text)."\n\n";
        }
        elseif ( $this->html ) {

            //Messages start with text/html alternatives in OB
            $message ="This is a multi-part message in MIME format.\n";
            $message.="\n--".$this->B1B."\n";

            $message.="Content-Type: multipart/related;\n\t boundary=\"".$this->B2B."\"\n\n";
            //plaintext section
            $message.="\n--".$this->B2B."\n";

            $message.="Content-Type: multipart/alternative;\n\t boundary=\"".$this->B3B."\"\n\n";
            //plaintext section
            $message.="\n--".$this->B3B."\n";

            $message.="Content-Type: text/plain; charset=\"iso-8859-1\"\n";
            $message.="Content-Transfer-Encoding: quoted-printable\n\n";
            // plaintext goes here
            $message.=$this->BodyLineWrap($this->text)."\n\n";

            // html section
            $message.="\n--".$this->B3B."\n";
            $message.="Content-Type: text/html; charset=\"iso-8859-1\"\n";
            $message.="Content-Transfer-Encoding: base64\n\n";
            // html goes here
            $message.=chunk_split(base64_encode($this->html))."\n\n";

            // end of text
            $message.="\n--".$this->B3B."--\n";

            // attachments html
            if (empty($this->htmlattachement)) {
                $message.="\n--".$this->B2B."--\n";
            } else {
                $message.=$this->writeattachement( $this->htmlattachement,$this->B2B);
            }

            // attachments
            if (empty($this->attachement)) {
                $message.="\n--".$this->B1B."--\n";
            } else {
                $message.=$this->writeattachement($this->attachement,$this->B1B);
            }

        }

        $this->body = $message;

        return $message;

    }

    // Mail Headers Methods

    function MakeHeaderField($Field,$Value) {
        return wordwrap($Field.": ".$Value, 78, "\n ")."\r\n";
    }

    function AddField2Header($Field,$Value) {
        $this->headers .= $this->MakeHeaderField($Field,$Value);
    }

    /**
     * builds mail headers from properties
     * @return string
     */
    function makeHeader() {
        $this->headers = '';
        if ( empty($this->subject) ) {
            $this->error_log("missing subject");
            return false;
        }
        //
        if ($this->set_mode!='php' ) {
            $this->AddField2Header("Subject", $this->subject);
        }

        # Date: Mon, 03 Nov 2003 20:48:06 +0100
        $this->AddField2Header("Date", date ('r'));

        if ( !empty($this->Xsender) )
            $this->AddField2Header("X-Sender",$this->Xsender);
        else
            $this->AddField2Header("X-Sender",$this->hfrom);

        if ( !empty($this->ErrorsTo) )
            $this->AddField2Header("Errors-To",$this->ErrorsTo);
        else
            $this->AddField2Header("Errors-To",$this->hfrom);

        if ( !empty($this->XMailer) )
            $this->AddField2Header("X-Mailer",$this->XMailer);

        if ( !empty($this->XPriority) )
            $this->AddField2Header("X-Priority",$this->XPriority);
        // from
        if ($this->from and $this->from->isValid()) {
            $this->AddField2Header("From",$this->from->email);
        } else {
            $this->error_log("no valid from !");
            return false;
        }

        if ( !empty($this->returnpath) ) $this->AddField2Header("Return-Path",$this->returnpath);

        // reply-to
        if ($this->reply)
            $this->AddField2Header("Reply-To",$this->reply->email);
        // mime, attachments
        $this->headers .="MIME-Version: 1.0\r\n";

        if ( !$this->html && $this->text && !empty($this->attachement) ) {
            $this->headers .= "Content-Type: multipart/mixed;\r\n\t boundary=\"".$this->B1B."\"\r\n";
        } elseif ( !$this->html && $this->text && empty($this->attachement) ) {
            $this->headers .="Content-Type: text/plain; charset=us-ascii; format=flowed\r\n";
            $this->headers .="Content-Transfer-Encoding: 7bit\r\n";
        } elseif ( $this->html ) {
            if ( !$this->text ) { $this->text="HTML only!"; }
            $this->headers .="Content-Type: multipart/mixed;\r\n\t boundary=\"".$this->B1B."\"\r\n";
        }
        /**
         * add carbon copies
         */
        if (!empty($this->cc)) {
            foreach ($this->cc as $cc)
                $this->AddField2Header("Cc",$cc->email);
        }
        if (!empty($this->bcc)) {
            foreach ($this->bcc as $bcc)
                $this->AddField2Header("Bcc",$bcc->email);
        }
        return $this->headers;
    }

    // Mail send by PHPmail
    /**
     * use php mail() to send email(s)
     * @return int successfully sent emails
     */
    function phpMail() {
        $headers=$this->makeHeader(); // echo $headers; exit;
        $sent=0;
        foreach ($this->to as $to) {
            if (mail($to->email,$this->subject,$this->body,$headers))
                $sent++;
            else
                $this->error_log("failed to send to {$to->email}");
        }
        return $sent;
    }

    // Socket Function

    function SocketStart() {
        if (!$this->connect = fsockopen (ini_get("SMTP"), ini_get("smtp_port"), $errno, $errstr, 30))  {
            $this->error_log("Could not talk to the sendmail server!"); return FALSE;
        };
        return fgets($this->connect, 1024);
    }

    function SocketStop() {
          fclose($this->connect);
        return TRUE;
    }

    function SocketSend($in,$wait='') {
        fputs($this->connect, $in, strlen($in));
        echo "-"; flush();
        if(empty($wait)) {
            $rcv = fgets($this->connect, 1024);
            return $rcv;
        }
        return TRUE;
    }

    // Mail Socket

    function socketmailstart() {

        $this->SocketStart();
        if (!isset($_SERVER['SERVER_NAME'])  || empty($_SERVER['SERVER_NAME'])) { $serv = 'unknown'; }
        else { $serv = $_SERVER['SERVER_NAME']; }
        $this->SocketSend("HELO $serv\r\n");
        }

    function socketmailsend($to) {

        $this->recipient = $to;
        $this->error_log("Socket vers $to");

        $this->SocketSend( "MAIL FROM:{$this->hfrom}\r\n" );
        $this->SocketSend( "RCPT TO:$to\r\n" );
        $this->SocketSend( "DATA\r\n" );
        $this->SocketSend( $this->CleanMailDataString($this->headers)."\r\n", 'NOWAIT' );
        $this->SocketSend( $this->CleanMailDataString($this->body)."\r\n", 'NOWAIT' );
        $this->SocketSend( ".\r\n" );
        $this->SocketSend( "RSET\r\n" );

        $this->error_log("Fin de l'envoi vers $to");

        return TRUE;
    }

    function socketmailstop() {
        $this->SocketSend("QUIT\r\n");
          $this->SocketStop();
        return TRUE;
    }

    function socketmailloop() {
        $this->socketmailstart();
        while ( list($key, $to) = each($this->recipientlist)) {
            $this->recipient = $to['mail'];
            $this->makeheader();
            $this->socketmailsend($to['mail']);
        }
        $this->socketmailstop();
        return true;
    }

    // Misc.

    function error_log($msg='') {
        if(!empty($msg)) {
                $this->error_log .= $msg . "\r\n--\r\n";
                return TRUE;
        }
        return " --- Error Log --- \r\n\r\n".$this->error_log;
    }

    function CleanMailDataString($data) {
        $data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data);
        $data = preg_replace("/\n\n/", "\n\r\n", $data);
        $data = preg_replace("/\n\./", "\n..", $data);
        return $data;
    }
}